iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0
Software Development

從 Fragment 到 Compose — 老 Android App 重寫的架構取捨系列 第 25 篇

Day 25|掃描結果怎麼回到呼叫端:SavedStateHandle 與共享 ViewModel 的取捨

  • 分享至 

  • xImage
  •  

動筆前先答(兩題)

1.【預測再驗證|UI 與版型】 昨天的 ScannerScreen 掃完要把 ScanResult 交回打開它的畫面。最常見的寫法是透過上一個 back stack entry 的 SavedStateHandle,骨架大概是這樣:

// 掃描畫面:按「完成」時
navController.previousBackStackEntry?.savedStateHandle?.set("scan_result", result)
navController.popBackStack()

// 呼叫端(例如任務明細):在自己的 composable<TaskDetailRoute> { entry -> ... } 裡
val result by entry.savedStateHandle
    .getStateFlow<ScanResult?>("scan_result", null)
    .collectAsStateWithLifecycle()
LaunchedEffect(result) {
    result?.let {
        viewModel.onScanned(it)
        entry.savedStateHandle.remove<ScanResult>("scan_result")
    }
}

下面每個情境,先寫下你的預測,再用實機或模擬器驗證(「開發者選項 → 不保留活動」、adb shell am kill <package> 都可以用來模擬 process 被回收):

  1. 掃完回到任務明細,接著旋轉螢幕。onScanned 總共被呼叫幾次?把 remove 那行拿掉,再旋轉一次、再從背景切回來,各是幾次?
  2. ScanResult 是一個普通的 data class(沒有 Parcelable、沒有 @Serializable)。哪一步會出問題:set 的當下、App 退到背景時、還是 process 被回收後回來時?換成 @Parcelize 之後呢?
  3. 連續掃描模式一次累積 500 筆條碼,整包放進 savedStateHandle。會不會出問題?如果會,是在哪個時間點、什麼徵狀?你的預測依據是什麼?
  4. 呼叫端改成在 ViewModel 裡讀結果,用 Hilt 注入給 ViewModel 的那個 SavedStateHandle,而不是 entry.savedStateHandle。讀得到嗎?這兩個是不是同一個物件?
  5. 配送員開著掃描畫面時收到推播,點下去被導到別的畫面、而且清掉了 back stack;接著再回到掃描畫面(例如從最近使用的 App 切回來)按「完成」。previousBackStackEntry 是誰?結果送到哪裡去了?

最後回答:這五個情境裡,哪幾個可以在 JVM 單元測試裡用 SavedStateHandle() 直接重現?哪幾個要起 TestNavHostController 或 instrumented test?哪幾個只能在實機驗、要寫進發版前的手動檢查清單?

我的回答:(待補)

2.【模擬面試追問|架構論述】 面試官看了你的掃描畫面設計,問:「掃描結果怎麼回到呼叫端?」你列出三種做法:

  • (A) 上一個 back stack entry 的 SavedStateHandle(第 1 題的寫法)。
  • (B) 共享 ViewModel:掃描畫面與呼叫端放在同一個巢狀導航圖裡,兩邊都用 hiltViewModel(parentEntry) 拿到綁在那個圖上的同一個 ViewModel,掃描畫面把結果寫進去。
  • (C) 全域的結果通道:一個 @Singleton 的物件裡放 MutableSharedFlow<ScanResult>,掃描畫面送出、呼叫端收集。

不看下面的大綱,依序回答面試官的追問:

  1. 你選哪一個?用一句話說理由。
  2. 掃描畫面要從三個不同的地方打開:任務明細、收款、盤點。在你選的做法裡,掃描畫面需不需要知道是誰打開它的?之後要新增第四個呼叫端,要改哪些地方?
  3. 如果選 (B):使用者掃完回到呼叫端後,這個共享 ViewModel 什麼時候被清掉?如果掃描畫面不在呼叫端所在的那個巢狀圖裡(例如它是從另一個分頁的圖打開的),會發生什麼事?
  4. 如果選 (C):back stack 上同時有兩個呼叫端都在等結果(任務 A 打開了掃描,掃描途中又從另一個入口打開一次),結果會送給誰?送出時呼叫端剛好不在前景(還沒開始收集)呢?process 被回收後呢?
  5. 你怎麼測「呼叫端收到結果後會做對的事」?三種做法各自需要準備什麼?哪一種完全不用起 NavController 就能測?
  6. 昨天 Day 24 第 2 題的「每掃一筆就向後端驗證」有三種做法(呼叫端自己驗證、ScanConfig 帶驗證函式、不做通用畫面),你預設選了其中一種。那個選擇跟今天選的回傳方式會互相牽動嗎?舉一個兩者合不來的組合。

最後用兩句話收尾這場面試:你的結論,以及你願意承認的代價。

我的回答:(待補)


上一篇
Day 24|13 個掃描 Activity 收成一個 ScannerScreen:ML Kit + CameraX
系列文
從 Fragment 到 Compose — 老 Android App 重寫的架構取捨 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言